﻿using FluentValidation;
using Furion.DatabaseAccessor;
using Furion.DataValidation;
using Furion.DynamicApiController;
using Furion.FriendlyException;
using FurStudy.Core;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace FurStudy.Application
{
    // 在 Furion 框架中，继承自 ControllerBase 类或 IDynamicApiController 接口或 [DynamicApiController] 特性都会被标记为控制器类型

    /// <summary>
    /// IDynamicApiController 方式
    /// </summary>
    public class FurionAppService : IDynamicApiController
    {
        public string GetName()
        {
            return $"Hello {nameof(Furion)}";
        }

        #region 多种请求谓词方法

        public string Get()
        {
            return $"GET 请求";
        }

        public string Post()
        {
            return $"Post 请求";
        }

        public string Delete()
        {
            return $"Delete 请求";
        }

        public string Put()
        {
            return $"Put 请求";
        }

        public string Patch()
        {
            return $"Patch 请求";
        }

        #endregion

        #region 多个自定义动作方法

        public string GetVerson()
        {
            return "V1.0.0";
        }

        public string ChangeProfile()
        {
            return "修改成功";
        }

        public string DeleteUser()
        {
            return "删除成功";
        }

        #endregion

        #region 带参数动作方法

        public string GetUser(int id)
        {
            return $"{id}";
        }

        public string GetUser(int id, string name)
        {
            return $"{id} {name}";
        }
        // 默认情况下，ASP.NET Core 会将 GET/HEAD 请求中的 类类型参数 设置为 [FromBody] 绑定
        public TestDto Add(TestDto testDto)
        {
            return testDto;
        }

        #endregion

        #region GET/HEAD类类型参数

        // 默认情况下，ASP.NET Core 会将 GET/HEAD 请求中的 类类型参数 设置为 [FromBody] 绑定
        // 但是，GET、HEAD 请求不支持 From Body 绑定。所以我们需要转换为 Query 查询参数。
        // Furion 框架支持以下两种方式配置：

        /// <summary>
        /// 配置一：[FromQuery] 特性
        /// </summary>
        /// <param name="testDto"></param>
        /// <returns></returns>
        public TestDto GetTest([FromQuery] TestDto testDto)
        {
            return testDto;
        }

        // 配置二：配置 DynamicApiControllerSettings --见Furion.Web.Entry/appsettings.json

        #endregion

        #region 自定义参数位置

        /// <summary>
        /// 参数默认为 ApiSeats.ActionEnd
        /// </summary>
        /// <param name="id"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        public string RouteSeat(int id, string name)
        {
            return "配置路由参数位置";
        }

        public string RouteSeat(
            [ApiSeat(ApiSeats.ControllerStart)] int id, // 控制器名称之前
            [ApiSeat(ApiSeats.ControllerEnd)] string name, // 控制器名称之后
            [ApiSeat(ApiSeats.ControllerEnd)] int age, // 控制器名称之后
            [ApiSeat(ApiSeats.ActionStart)] decimal weight, // 动作方法名称之前
            [ApiSeat(ApiSeats.ActionStart)] float height, // 动作方法名称之前
            [ApiSeat(ApiSeats.ActionEnd)] DateTime birthday) // 动作方法名称之后（默认值）)
        {
            return "配置路由参数位置";
        }

        // [注] 多个同位置配置的参数将按照定义参数顺序进行排序。
        // [ApiSeat]只能应用于贴了[FromRoute]特性的参数或基元类型、值类型、可空基元类型和可空值类型。

        #endregion

        #region 自定义请求谓词

        [HttpPost]
        public string GetVersion()
        {
            return "1.0.0";
        }

        #endregion

        #region 支持多个谓词

        /// <summary>
        /// AcceptVerbs：指定操作支持的http方法
        /// </summary>
        /// <returns></returns>
        [HttpPost, HttpGet, AcceptVerbs("PUT", "DELETE")]
        public string GetVersion2()
        {
            return "1.0.0";
        }

        // [注] 如果动作方法中含有类类型参数，且含有 POST/PUT/DELETE 任意请求谓词，那么该参数会自动添加[FromBody]参数，即使在GET/HEAD请求中不支持

        #endregion

        #region 不公开控制器或动作方法

        /// <summary>
        /// 推荐控制器或动作方法设置不导出使用[ApiDescriptionSettings(false)]特性。该特性默认继承自ApiExplorerSettingsAttribute类
        /// </summary>
        /// <returns></returns>
        [ApiDescriptionSettings(false)]
        // [ApiExplorerSettings(IgnoreApi = true)]
        public string NotPublicControllerOrAction()
        {
            return "不公开控制器或动作方法";
        }

        #endregion

        #region 保持控制器方法和命名

        // 默认情况下，动态API会将控制器和方法名输出为RESTFul风格的路由，如需保留原有设计，只需配置

        //{
        //    "DynamicApiControllerSettings": {
        //    "KeepName": true,
        //    "KeepVerb": true,
        //    "LowercaseRoute": false
        //}
        #endregion

        #region 方法参数 [FromQuery] 化/参数非必填/参数可选

        /// <summary>
        /// 默认情况下，所有的基元类型参数都会贴上[FromRoute]特性，如果需要将参数调整为[FromQuery]修饰，只需要在方法上面贴[QueryParameters]特性即可
        /// </summary>
        /// <param name="id"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        [QueryParameters]
        public string GetWithFromQuery(int id, string name)
        {
            string str = $"{id} {name}";
            return nameof(str); // nameof 表达式可生成变量、类型或成员的名称作为字符串常量
        }
        // 上述方法生成的路由为：https://xxx.com?id=1&name=Furion

        // 如果不喜欢每个都配置，也可以全局配置（只会影响基元类型的参数）：
        //{
        //    "DynamicApiControllerSettings": {
        //    "UrlParameterization": true
        //}

        // [注] 贴了[QueryParameters]之后，会对所有参数影响，包括类类型参数，如果不需要处理某个参数，只需要贴[FromXXX]特性即可。

        #endregion

        #region 路由参数非必填/选填

        /// <summary>
        /// 方式一：通过可空 ？
        /// </summary>
        /// <param name="id"></param>
        /// <param name="dateTime"></param>
        /// <returns></returns>
        public object Method1(int id, DateTime? dateTime)
        {
            return null;
        }

        /// <summary>
        /// 方式二：通过默认值
        /// </summary>
        /// <param name="id"></param>
        /// <param name="age"></param>
        /// <returns></returns>
        public object Method2(int id, int age = 10)
        {
            return null;
        }

        /// <summary>
        /// 方式三：默认值 + 可空 ？
        /// </summary>
        /// <param name="id"></param>
        /// <param name="age"></param>
        /// <returns></returns>
        public object Method3(int id, int? age = 10)
        {
            return null;
        }

        /// <summary>
        /// 方式四：[FromQuery] 修饰
        /// </summary>
        /// <param name="id"></param>
        /// <param name="keyword"></param>
        /// <returns></returns>
        public object Method4(int id, [FromQuery] string keyword)
        {
            return null;
        }

        #endregion

        #region [FromRoute]路由约束

        /// <summary>
        /// 最小值10
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public object Mehthod5([RouteConstraint(":min(10)")] int id)
        {
            return null;

            // RouteConstraint其他约束见地址：https://dotnetchina.gitee.io/furion/docs/dynamic-api-controller/#5817-formroute-%E8%B7%AF%E7%94%B1%E7%BA%A6%E6%9D%9F
        }

        #endregion

        #region Sql代理

        #region 构造函数方式

        private readonly ISql _sql;
        public FurionAppService(ISql sql)
        {
            _sql = sql;
        }

        #endregion

        #region 方法参数注入

        //public async Task<List<TestDto>> GetAll([FromServices] ISql, string keyword)
        //{
        //}

        #endregion

        #region Db.GetSqlDispatchProxy<ISql>()

        //var sql = Db.GetSqlDispatchProxy<ISql>();

        #endregion

        #endregion
    }

    #region 支持自定义路由

    /// <summary>
    /// 自定义控制器路由
    /// </summary>
    [Route("customapi/mobile/[controller]")]
    public class FurionAppServiceByController : IDynamicApiController
    {
        public string GetVersion()
        {
            return "1.0.0";
        }
    }

    /// <summary>
    /// 自定义方法路由
    /// </summary>
    public class FurionAppServiceByAction : IDynamicApiController
    {
        [Route("customapi/[action]")]
        public string GetVersion()
        {
            return "1.0.0";
        }
    }

    /// <summary>
    /// 同时自定义路由（控制器路由+方法路由）
    /// </summary>
    [Route("customapi/mobile/[controller]")]
    public class FurionAppServiceByMeanWhile : IDynamicApiController
    {
        [Route("get/[action]")]
        public string GetVersion()
        {
            return "1.0.0";
        }
    }

    /// <summary>
    /// 谓词自定义路由
    /// </summary>
    [Route("api/[controller]")]
    public class FurionAppServiceByPredicate : IDynamicApiController
    {
        [Route("get/[action]")]
        public string GetVersion()
        {
            return "1.0.0";
        }
    }

    // Tips：动作方法自定义路由如果以/开头，则不会合并控制器路由。

    // 推荐配置：自定义路由如果需要用到控制器/动作方法名称，推荐使用[controller]或[action]占位符，因为该占位符已经自动处理了前后缀、版本号、模块名称等。

    #endregion

    #region 多路由随意组合

    [Route("api/[controller]")]
    [Route("api/[controller]/second")]
    [Route("api/[controller]/three")]
    public class FurionAppServiceByManyRoute : IDynamicApiController
    {
        [HttpGet]
        [HttpGet("get/[action]")]
        [HttpPost]
        [HttpPost("post/cus-version")]
        public string GetVersion()
        {
            return "1.0.0";
        }
    }

    // [注] 动作方法不能同时贴[Route]和[HttpMethod]特性，只能二取一

    #endregion

    #region 支持版本控制

    #region 控制器版本

    public class FurionAppServiceV1 : IDynamicApiController
    {
        public string Get()
        {
            return nameof(Furion);
        }
    }

    public class FurionAppServiceV1_2 : IDynamicApiController
    {
        public string Get()
        {
            return nameof(Furion);
        }
    }

    public class FurionAppServiceV1_2_1 : IDynamicApiController
    {
        public string Get()
        {
            return nameof(Furion);
        }
    }

    #endregion

    #region 动作方法版本

    public class FurionAppServiceByActionVersion : IDynamicApiController
    {
        public string Get()
        {
            return nameof(Furion);
        }

        public string GetV1()
        {
            return nameof(Furion);
        }
        public string GetV2_1()
        {
            return nameof(Furion);
        }
    }

    #endregion

    // 版本生成原理：V[0-9_]结尾的命名自动解析成版本号，如FurionAppServiceV2 -> Furion@2。

    /// <summary>
    /// 版本复写
    /// 除了通过特定后缀方式以外，版本还直接通过[ApiDescriptionSettings]进行复写
    /// </summary>
    public class FurionAppServiceByVersionReWrite
    {
        /// <summary>
        /// 这时，生成版本将采用4.0替代1
        /// </summary>
        /// <returns></returns>
        [ApiDescriptionSettings(Version = "4.0")]
        public string GetV1()
        {
            return nameof(Furion);
        }
    }

    #endregion

    #region ApiDescriptionSettings

    #region Name配置

    /// <summary>
    /// Name参数可以覆盖动态WebAPI自动生成的控制器或动作方法名称
    /// </summary>
    [ApiDescriptionSettings(Name = "MyFur")]
    public class FurionAppService9 : IDynamicApiController
    {
        [ApiDescriptionSettings(Name = "MyGet")]
        public string Get()
        {
            return nameof(Furion);
        }

        // 生成路由地址：api/myfur/myget
    }

    #endregion

    #region KeepName配置

    /// <summary>
    /// KeepName参数可以保留原有的控制器或动作方法名称
    /// </summary>
    [ApiDescriptionSettings(KeepName = true)]
    public class FurionAppService10 : IDynamicApiController
    {
        [ApiDescriptionSettings(KeepName = true)]
        public string Get()
        {
            return nameof(Furion);
        }

        // 生成路由地址：api/furappservice/get
    }


    #endregion

    #region SplitCamelCase配置

    /// <summary>
    /// SplitCamelCase参数默认将骆驼(驼峰)命名切割成多个单词并通过指定占位符连接起来。默认占位符为-。默认为true
    /// </summary>
    [ApiDescriptionSettings(SplitCamelCase = false)]
    public class MyFurionAppService2 : IDynamicApiController
    {
        [ApiDescriptionSettings(SplitCamelCase = true)]
        public string ChangeUserName()
        {
            return nameof(Furion);
        }

        // 生成路由地址：api/myfur/change-user-name
    }

    #endregion

    // 特别注意：KeepName优先级高于SplitCamelCase，也就是KeepName设置为 true，则不会处理SplitCamelCase参数。

    #region KeepVerb配置

    /// <summary>
    /// KeepVerb参数作用于动作方法，标识是否保留动作谓词
    /// </summary>
    public class FurionAppService5 : IDynamicApiController
    {
        [ApiDescriptionSettings(KeepVerb = true)]
        public string GetVersion()
        {
            return nameof(Furion);
        }

        // 生成路由地址：/api/fur/get-version
    }

    #endregion

    #region Enabled配置

    /// <summary>
    /// Enabled参数配置接口是否导出。通常用于动作方法，如果用于控制器实际作用不大。
    /// </summary>
    public class FurionAppService11 : IDynamicApiController
    {
        public string GetVersion()
        {
            return nameof(Furion);
        }

        [ApiDescriptionSettings(false)]
        public string NoExport()
        {
            return nameof(Furion);
        }

        // 生成路由地址：api/fur/version  noexport方法不导出
    }

    #endregion

    #region Module配置

    /// <summary>
    /// Module参数可以配置路由分离，类似于Mvc区域的作用
    /// </summary>
    [ApiDescriptionSettings(Module = "mobile")]
    public class FurionAppService12 : IDynamicApiController
    {
        [ApiDescriptionSettings(Module = "user")]
        public string GetVersion()
        {
            return nameof(Furion);
        }

        // 生成路由地址：api/mobile/fur/user/version
    }

    #endregion

    #region Version配置

    /// <summary>
    /// Version参数可以配置接口版本，同时又可以复写特殊版本命名配置。默认版本分隔符为@
    /// </summary>
    [ApiDescriptionSettings(Version = "1.0")]
    public class FurionAppService13 : IDynamicApiController
    {
        // V2.0.0 被复写成 V2.1.1
        [ApiDescriptionSettings(Version = "2.1.1")]
        public string GetVersion2_0_0()
        {
            return nameof(Furion);
        }

        // 生成路由地址：api/fur@1.0/version@2.1.1
    }

    #endregion

    #region Groups配置

    /// <summary>
    /// Groups配置主要用于配置Swagger分组信息。
    /// 通过配置Groups参数可以将控制器和动作方法进行归类和多个分组直接共享。
    /// 可通过[ApiDescriptionSettings(params Groups)]构造函数传入或指定Groups参数配置接口是否导出。
    /// 通常用于动作方法，如果用于控制器实际作用不大。
    /// </summary>
    [ApiDescriptionSettings("Default", "Common")]
    public class FurionAppService14 : IDynamicApiController
    {
        public string Get()
        {
            return nameof(Furion);
        }

        [ApiDescriptionSettings("Custom")]
        public int Get(int id)
        {
            return id;
        }
    }

    #endregion

    #region Tag配置

    /// <summary>
    /// 未贴标签之前
    /// </summary>
    public class FurionAppService15 : IDynamicApiController
    {
        public string Get()
        {
            return nameof(Furion);
        }

        public int Get(int id)
        {
            return id;
        }

        // 生成路由地址：api/fur@1.0/version@2.1.1
    }

    /// <summary>
    /// 贴标签之后
    /// </summary>
    [ApiDescriptionSettings(Tag = "分组一")]
    public class FurionAppService16 : IDynamicApiController
    {
        public string Get()
        {
            return nameof(Furion);
        }

        public int Get(int id)
        {
            return id;
        }
    }

    // Tips：如果Tag名字一样，则会自动合并，否则只是命名。

    #endregion

    #endregion

    #region 规范化文档(Swagger)

    #region 多分组支持

    [ApiDescriptionSettings("Group1")]
    public class FurionAppService17 : IDynamicApiController
    {
        /// <summary>
        /// 随父类Group1分组
        /// </summary>
        /// <returns></returns>
        public string Post()
        {
            return nameof(Furion);
        }

        /// <summary>
        /// 在Group1和Group3都有我
        /// </summary>
        /// <returns></returns>
        [ApiDescriptionSettings("Group1", "Group3")]
        public string Get()
        {
            return nameof(Furion);
        }

        /// <summary>
        /// 我只在Group2出现
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [ApiDescriptionSettings("Group2")]
        public int Get(int id)
        {
            return id;
        }
    }

    #endregion

    #region 多分组排序

    // 方式一：通过分组名称添加@整数进行排序

    /// <summary>
    /// 可以通过在分组名后面添加@整数进行排序，整数越大排前面。如果分组名称多次指定且多次指定了@整数，则自动取该分组最大的整数进行排序。
    /// </summary>
    [ApiDescriptionSettings("Group1@1")]
    public class FurionAppService18 : IDynamicApiController
    {
        /// <summary>
        /// 随父类Group1分组
        /// </summary>
        /// <returns></returns>
        public string Post()
        {
            return nameof(Furion);
        }

        /// <summary>
        /// 在Group1和Group3都有我
        /// </summary>
        /// <returns></returns>
        [ApiDescriptionSettings("Group1", "Group3")]
        public string Get()
        {
            return nameof(Furion);
        }

        /// <summary>
        /// 我只在Group2出现
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [ApiDescriptionSettings("Group2@2")]
        public int Get(int id)
        {
            return id;
        }

        // 方式二：通过配置文件配置排序 见FurStudy.Web.Entry - appsettings.json

        // 两次排序结果相同：Group2、Group1、Group3

        // 排序说明：分组默认排序Order为0。如果同时配置了@整数和appsettings.json配置文件，那么优先采用appsettings.json中的Order
    }


    #endregion

    #region 设置Example Value默认值

    /// <summary>
    /// Swagger会自动根据对象类型输入参数添加Example Value默认值，
    /// 但是该默认值通常是对象属性的类型字符串或缺省值，
    /// 如果我们需要自定义这些默认值，只需要添加 /// <example>默认值</example> 注释即可。
    /// 更多类型默认值设置示例：
    /// bool 类型：/// <example>true</example>
    /// string 类型：/// <example>"foobar"</example>
    /// number 类型：/// <example>123</example>
    /// null 类型： /// <example>null</example>
    /// array 类型：/// <example>[ 1, 2, 3 ]</example>
    /// </summary>
    public class FurionAppService19 : IDynamicApiController
    {
        /// <summary>
        /// 年龄
        /// </summary>
        /// <example>13</example>
        [Required, Range(10,110)]
        public int Age { get; set; }
    }

    #endregion

    #endregion

    #region 友好异常处理

    public class FurionAppService20 : IDynamicApiController
    {
        // Tip：[ErrorCodeType]和IErrorCodeTypeProvider和appsettings.json可以同时使用

        /// <summary>
        /// 抛出异常
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public int Get(int id)
        {
            // 简单抛个异常
            if (id < 3)
            {
                throw Oops.Oh($"{id} 不能小于3");
            }

            // 抛出特定类型异常
            if (id < 6)
            {
                throw Oops.Oh($"{id} 不能小于3。", typeof(InvalidOperationException));
            }

            // 静态异常类使用
            if (id < 9)
            {
                throw Oops.Oh(ErrorCodes.z1000, id, 3);
            }

            return id;
        }

        /// <summary>
        /// 抛出异常 - 更多例子
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public int Get2(int id)
        {
            if (id == 1)
            {
                throw Oops.Oh(1000);
                throw Oops.Oh(ErrorCodes.x1000);
                throw Oops.Oh("哈哈哈哈哈哈");
                throw Oops.Oh(errorCode: "x1001");
                throw Oops.Oh(1000, typeof(Exception));
                throw Oops.Oh(1000).StatusCode(400); // 设置错误码
                throw Oops.Oh("用户名或密码错误");
                throw Oops.Bah(1000);

                throw Oops.Oh(ErrorCodes.z1001); // 自动应用多语言
            }
            return id;
        }

        /// <summary>
        /// appsetting.json配置使用
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public int Get3(int id)
        {
            if (id < 6)
            {
                throw Oops.Oh(5000, id, 6); // 5000已在appsetting.json中配置
            }
            return id;
        }

        /// <summary>
        /// [IfException]特性可以覆盖默认消息配置。也就是覆盖异常消息类型和appsettings.json中的配置
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [IfException(ErrorCodes.z1000, ErrorMessage = "我覆盖了默认的：{0} 不能小于 {1}")]
        public int Get4(int id)
        {
            if (id < 3)
            {
                throw Oops.Oh(ErrorCodes.z1000, id, 3);
            }

            return id;
        }

        /// <summary>
        /// 格式化流程：如果消息内容中包含格式化占位符但未指定格式化参数，那么会查找异常所在方法是否贴有[IfException]特性且含有格式化参数，接着就会查找Oops.Oh中指定的格式化参数
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [IfException(typeof(ExceptionType), ErrorMessage = "特定异常类型全局拦截")]
        [IfException(ErrorMessage = "全局异常拦截")]
        [IfException(ErrorCodes.z1000, ErrorMessage = "我覆盖了默认的：{0} 不能小于 {1}")]
        [IfException(ErrorCodes.x1001, "格式化参数1", "格式化参数2", ErrorMessage = "我覆盖了默认的：{0} 不能小于 {1}")]
        [IfException(ErrorCodes.x1000, "格式化参数1", "格式化参数2")]
        [IfException(ErrorCodes.SERVER_ERROR, "格式化参数1", "格式化参数2")]
        public int Get5(int id)
        {
            if (id < 3)
            {
                throw Oops.Oh(ErrorCodes.z1000, id, 3);
            }

            return id;
        }

        // 异常消息优先级
        // [ErrorCodeItemMetadata] -> appsettings.json -> [IfException]。（低 -> 高）
        // [IfException]会覆盖appsettings.json定义的状态码消息。
        // appsettings.json会覆盖[ErrorCodeItemMetadata]定义的消息。
    }

    #endregion

    #region 数据校验

    #region 兼容Mvc特性验证

    public class FurionAppService21 : IDynamicApiController
    {
        /// <summary>
        /// 值类型验证
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public int Get(int id)
        {
            return id;
        }

        /// <summary>
        /// 对象类型验证
        /// </summary>
        /// <param name="testDto"></param>
        /// <returns></returns>
        public TestDto Post(TestDto testDto)
        {
            return testDto;
        }
    }

    #endregion

    #region 手动验证

    /// <summary>
    /// 验证模型
    /// 支持Mvc内置的特性验证、属性验证及复杂的IValidatableObject验证
    /// </summary>
    public class FurionAppService22 : IDynamicApiController
    {
        [NonValidation] // 跳过全局验证
        public DataValidationResult Post(TestDto testDto)
        {
            return testDto.TryValidate();
        }
    }

    #endregion

    #region ValidationTypes常见验证

    /// <summary>
    /// 单个类型验证
    /// </summary>
    public class FurionAppService23 : IDynamicApiController
    {
        public DataValidationResult Post(TestDto testDto)
        {
            // 验证中文
            "我叫Monk".TryValidate(ValidationTypes.Chinese); // => false

            // 验证数值
            2.TryValidate(ValidationTypes.Numeric); // => true

            // 验证整数
            true.TryValidate(ValidationTypes.Integer); // => false

            // 验证邮箱
            "monksoul@outlook.com".TryValidate(ValidationTypes.EmailAddress); // => true

            // 验证负数
            2.0m.TryValidate(ValidationTypes.NegativeNumber); // => false

            // 自定义正则表达式验证
            "Furion".TryValidate("/^Furion$"); // => true (匹配Furion的值)

            return testDto.TryValidate();
        }
    }

    /// <summary>
    /// 多个组合类型验证
    /// </summary>
    public class FurionAppService24 : IDynamicApiController
    {
        public DataValidationResult Post(TestDto testDto)
        {
            // 验证数值类型且是整数
            "20".TryValidate(ValidationTypes.Numeric, ValidationTypes.Integer); // => true

            // 验证是日期或时间格式
            "2020-05-20".TryValidate(ValidationPattern.AtLeastOne, ValidationTypes.Date, ValidationTypes.Time); // => true
            // 可通过设置TryValidate([ValidationPattern], params object[] validationTypes)方法的ValidationPattern参数配置验证逻辑，如：同时成立或只要一个成立即可验证通过
            "23:45:20".TryValidate(ValidationPattern.AtLeastOne, ValidationTypes.Date, ValidationTypes.Time); // => true

            return testDto.TryValidate();
        }
    }

    /// <summary>
    /// 自定义ValidationTypes类型
    /// </summary>
    public class FurionAppService25 : IDynamicApiController
    {
        public DataValidationResult Post(TestDto testDto)
        {
            "q1w2e3".TryValidate(MyValidationTypes.StrongPassword); // => false

            "furos".TryValidate(MyValidationTypes.StartWithFurString); // => true

            "Q1w2e3r4t5!*".TryValidate(MyValidationTypes.StrongPassword, ValidationTypes.EmailAddress); // => true

            return testDto.TryValidate();
        }

        // 特别注意：自定义的验证类型也要保证名称全局唯一，也就是多个验证类型不能出现一样的名字

        // 错误消息查找优先级  DefaultErrorMessage -> IValidationMessageTypeProvider -> appsettings.json(低 -> 高)
    }
    #endregion

    #region 模型验证范围

    /// <summary>
    /// 局部验证
    /// </summary>
    public class FurionAppService26 : IDynamicApiController
    {
        /// <summary>
        /// [TypeFilter(typeof(DataValidationFilter))]局部验证
        /// </summary>
        /// <param name="testDto"></param>
        /// <returns></returns>
        [TypeFilter(typeof(DataValidationFilter))]
        public TestDto Post(TestDto testDto)
        {
            return testDto;
        }
    }

    #endregion

    #region 集成FluentValidation第三方校验

    public class Person {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Email { get; set; }
        public int Age { get; set; }
    }

    public class PersonValidator : AbstractValidator<Person>
    {
        public PersonValidator() {
            RuleFor(x => x.Id).NotNull();
            RuleFor(x => x.Name).Length(0, 10);
            RuleFor(x => x.Email).EmailAddress();
            RuleFor(x => x.Age).InclusiveBetween(18, 60);
        }
    }

    #endregion

    #endregion

    #region 视图最佳读取方式

    /// <summary>
    /// 由于视图是虚拟表，不应该对其进行写操作，所以应该采用 只读仓储初始化视图
    /// 通过.Constraint<TEntity,TDbContextLocator>方法可以将仓储约束为特定仓储，如只读仓储，可读可写仓储，只新增仓储等
    /// </summary>
    public class FurionAppService27 : IDynamicApiController
    {
        //private readonly IReadableRepository<V_Person> _readableRepository;

        //public FurionAppService27(IRepository<V_Person> repository)
        //{
        //    // 初始化只读仓储
        //    _readableRepository = repository.Constraint<IReadableRepository<V_Person>>();
        //}

        ///// <summary>
        ///// 读取视图
        ///// </summary>
        ///// <returns></returns>
        //public async Task<List<V_Person>> GetVPerson()
        //{
        //    var list = await _readableRepository.AsQueryable().ToListAsync();
        //    return list;
        //}
    }

    #endregion

    /// <summary>
    /// [DynamicApiController] 方式
    /// </summary>
    [DynamicApiController]
    public class FurionAppService2
    {
        public string Get()
        {
            return $"Hello {nameof(Furion)}";
        }
    }
}
